#include <my_head.h>

int main(int argc, const char *argv[]){
    if(3 != argc){
        printf("Usage : %s <IP> <PORT>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    char file_name[32] = {0};
    int nbytes = 0;
    char buff[600] = {0};
    unsigned short code = 0; // 操作码
    unsigned short num = 0;  // 块编号 或者 差错码
    int recv_count = 0; // 用来记录已经收到的块的编号
    int fd = 0;

    // 创建套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == sockfd)
        ERR_LOG("socket error");
    
    // 填充 服务器! 网络信息结构体
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
RE_NAME: // 要注意 读写请求是要发给 69 端口的
    serveraddr.sin_port = htons(atoi(argv[2]));  // 端口号要填 69
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t serveraddr_len = sizeof(serveraddr);

    printf("请输入要下载的文件名:");
    fgets(file_name, sizeof(file_name), stdin);
    file_name[strlen(file_name)-1] = '\0';
    
    // 组装数据包
    nbytes = sprintf(buff, "%c%c%s%c%s%c", 0, 1, file_name, 0, "octet", 0);

    // 给服务器发送下载请求的数据包
    if(-1 == sendto(sockfd, buff, nbytes, 0, (struct sockaddr *)&serveraddr, serveraddr_len)){
        ERR_LOG("sendto error");
    }
    while(1){
        // 接收服务器发回来的消息(此处需要保存服务器的网络信息结构体 因为临时端口号在里面呢)
        if(-1 == (nbytes = recvfrom(sockfd, buff, sizeof(buff), 0, (struct sockaddr *)&serveraddr, &serveraddr_len))){
            ERR_LOG("recvfrom error");
        }

        // 解析服务器发来的内容
        code = ntohs(*(unsigned short *)buff);
        num = ntohs(*(unsigned short *)(buff+2));

        if(5 == code){ // 说明出错了
            if(1 == num){
                printf("文件 [%s] 不存在\n", file_name);
                goto RE_NAME;
            }else{
                printf("error : errno = [%d]  errstr = [%s]\n", num, buff+4);
                exit(EXIT_FAILURE);
            }
        }else if(3 == code && num == recv_count+1){ // 说明服务器已经发来文件内容了
            recv_count++; // 为了保证不受到重复的数据包的
            if(1 == num){
                // 第一次收到文件内容时需要打开文件
                if(-1 == (fd = open(file_name, O_WRONLY|O_CREAT|O_TRUNC, 0664)))
                    ERR_LOG("open error");
            }
            // 将收到的文件内容写入本地文件
            write(fd, buff+4, nbytes-4);
            // 组装ADK
            *(unsigned short *)buff = htons(4);
            *(unsigned short *)(buff+2) = htons(num);
            // 给服务器发送ACK
            if(-1 == sendto(sockfd, buff, 4, 0, (struct sockaddr *)&serveraddr, serveraddr_len)){
                ERR_LOG("sendto error");
            }
            // 判断是否收完
            if(nbytes-4 < 512){
                break;
            }
        }
    }
    close(fd);
    printf("文件 [%s] 下载成功\n", file_name);
    
    // 关闭套接字
    close(sockfd);

    return 0;
}